#ifndef __WIZ_UTILS_STATE_MACHINE_PLANNED_HPP__SHANHAOBO_19800429__
#define __WIZ_UTILS_STATE_MACHINE_PLANNED_HPP__SHANHAOBO_19800429__

#include "../../Core/Container/WizDataTypeMap.hpp"
#include "../../Core/Container/WizDataTypeSet.hpp"

#include "../../DataType/SmartPtr/SharedPtr/WizDataTypeSharedPtr.hpp"

#include "./WizUtilsStateMachineStatePlanned.hpp"

namespace Wiz
{
    namespace StateMachinePlanned
    {
        template <class EventT, class NameT>
        class Type
        {
            WIZ_DECLARE_CLASS_THIS(Type);
        protected:
            typedef EventT                                          tEvent;
            typedef NameT                                           tName;
        public:
            typedef ::Wiz::StateMachinePlanned::State::Type<tEvent, tName> tState;
        protected:
            typedef ::Wiz::SharedPtr::Type<tState>                  tStatePtr;
            typedef ::Wiz::Map::Type<tName, tStatePtr>              tStateMap;
        public:
            Type()
            {
            }
            virtual ~Type()
            {
            }
        public:
            template<class StateT>
            tStatePtr CreateState(const tName& InName)
            {
                if (this->HasState(InName))
                {
                    return tStatePtr();
                }
                return tStatePtr(new StateT(*this, InName));
            }
        public:
            template<class StateT>
            ::Wiz::Bool::Type Clone(tThisIn InOther)
            {
                tStateMap::tIteratorConst Curr;
                tStateMap::tForEachConst FE(Curr, InOther.m_StateMap);

                while (FE.HasMoreElement())
                {
                    tStatePtr SPtr = this->CreateState<StateT>(Curr.GetIndex());
                    if (SPtr.NotValid())
                    {
                        WIZ_ASSERT(::Wiz::Bool::False);
                        return ::Wiz::Bool::False;
                    }
                    SPtr->Clone(Curr.GetValue().Get());
                }

                return ::Wiz::Bool::True;
            }

        public:
            ::Wiz::Bool::Type AccpetEvent(const tEvent& InEvt)
            {
                WIZ_ASSERT(m_CurrState.IsValid());

                /// ============================================= ///
                tName NextStateName;
                if (m_CurrState->FilterEvent(InEvt) && m_CurrState->GetNextStateName(InEvt, NextStateName))
                {
                    return GotoState(NextStateName);
                }
                /// ============================================= ///

                return ::Wiz::Bool::False;
            }

            ::Wiz::Bool::Type GotoState(const tName& InName)
            {
                tStatePtr SPtr = GetStateByName(InName);
                if (SPtr.NotValid())
                {
                    return ::Wiz::Bool::False;
                }

                if (SPtr->Enter())
                {
                    if (m_CurrState.IsValid())
                    {
                        m_CurrState->Exit();
                        m_CurrState.Reset();
                    }
                    m_CurrState = SPtr;
                    return ::Wiz::Bool::True;
                }

                return ::Wiz::Bool::False;
            }

            ::Wiz::Bool::Type GotoState(tStatePtr SPtr)
            {
                if (SPtr.NotValid())
                {
                    return ::Wiz::Bool::False;
                }

                if (this->HasState(SPtr) == ::Wiz::Bool::False)
                {
                    return ::Wiz::Bool::False;
                }

                if (SPtr->Enter())
                {
                    if (m_CurrState.IsValid())
                    {
                        m_CurrState->Exit();
                        m_CurrState.Reset();
                    }
                    m_CurrState = SPtr;
                    return ::Wiz::Bool::True;
                }

                return ::Wiz::Bool::False;
            }
        public:
            ::Wiz::Bool::Type AddEvent(const EventT& InEvt, const tName& From, const tName& To)
            {
                tStateMap::tIteratorConst ItrFrom = m_StateMap.Find(From);
                if (ItrFrom == m_StateMap.End())
                {
                    WIZ_ASSERT(::Wiz::Bool::False);
                    return ::Wiz::Bool::False;
                }
                tStatePtr FromPtr = ItrFrom.GetValue();
                if (FromPtr.NotValid())
                {
                    WIZ_ASSERT(::Wiz::Bool::False);
                    return ::Wiz::Bool::False;
                }

                tStateMap::tIteratorConst ItrTo = m_StateMap.Find(To);
                if (ItrTo == m_StateMap.End())
                {
                    WIZ_ASSERT(::Wiz::Bool::False);
                    return ::Wiz::Bool::False;
                }
                tStatePtr ToPtr = ItrTo.GetValue();
                if (ToPtr.NotValid())
                {
                    WIZ_ASSERT(::Wiz::Bool::False);
                    return ::Wiz::Bool::False;
                }

                return FromPtr->AddNextState(InEvt, ToPtr.Get());
            }
            ::Wiz::Bool::Type AddEvent(const EventT& InEvt, tStatePtr From, tStatePtr To)
            {
                WIZ_ASSERT(From.IsValid());
                WIZ_ASSERT(To.IsValid());

                if (m_StateMap.HasState(From->GetName()))
                {
                    return From->AddNextState(InEvt, To->GetName());
                }

                WIZ_ASSERT(::Wiz::Bool::False);
                return ::Wiz::Bool::False;
            }
        public:
            tStatePtr GetInitState()
            {
                return m_InitState;
            }

            ::Wiz::Bool::Type SetInitState(const tName& InName)
            {
                tStateMap::tIteratorConst Itr = m_StateMap.Find(InName);
                if (Itr == m_StateMap.End())
                {
                    WIZ_ASSERT(::Wiz::Bool::False);
                    return ::Wiz::Bool::False;
                }

                if (Itr.GetValue()->Enter() == ::Wiz::Bool::False)
                {
                    WIZ_ASSERT(::Wiz::Bool::False);
                    return ::Wiz::Bool::False;
                }

                m_InitState = Itr.GetValue();
                m_CurrState = Itr.GetValue();

                return ::Wiz::Bool::True;
            }

            ::Wiz::Bool::Type SetInitState(tStatePtr SPtr)
            {
                WIZ_ASSERT(::Wiz::IsNull(SPtr));

                return this->SetInitState(SPtr->GetName());
            }

            ::Wiz::Bool::Type AddState(tStatePtr s)
            {
                return m_StateMap.Insert(s->GetName(), s) != m_StateMap.End();
            }
        public:
            tStatePtr GetCurrState()
            {
                return m_CurrState;
            }
            ::Wiz::Void::Type SetCurrState(const tName& InName)
            {
                WIZ_ASSERT(::Wiz::IsNull(SPtr));

                tStateMap::tIteratorConst Itr = m_StateMap.Find(InName);
                if (Itr == m_StateMap.End())
                {
                    WIZ_ASSERT(::Wiz::Bool::False);
                    return;
                }

                m_CurrState = Itr.GetValue();
            }
        public:
            tStatePtr GetStateByName(const tName& InName) const
            {
                tStateMap::tIteratorConst Itr = m_StateMap.Find(InName);
                if (Itr == m_StateMap.End())
                {
                    return tStatePtr();
                }

                return Itr.GetValue();
            }
        public:
            ::Wiz::Bool::Type HasState(tStatePtr SPtr) const
            {
                WIZ_ASSERT(::Wiz::IsValidPtr(SPtr));
                return this->HasState(SPtr->GetName());
            }
            ::Wiz::Bool::Type HasState(const tName& InName) const
            {
                return m_StateMap.Find(InName) != m_StateMap.End();
            }
        public:
            ::Wiz::Void::Type Remove(tStatePtr SPtr)
            {
                WIZ_ASSERT(::Wiz::IsValidPtr(SPtr));
                this->Remove(SPtr->GetName());
            }
            ::Wiz::Void::Type Remove(const tName& InName)
            {
                m_StateMap.Remove(InName);
            }
        protected:
            tStateMap       m_StateMap;
            tStatePtr       m_CurrState;
            tStatePtr       m_InitState;
        };
    } /// end of namespace StateMachinePlanned
} /// end of namespace Wiz


#endif /*__WIZ_UTILS_STATE_MACHINE_PLANNED_HPP__SHANHAOBO_19800429__*/
